package xj.graph2d.tools;

import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ListIterator;
import java.util.Observer;

import xj.graph2d.Command;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.DrawShapeListener;
import xj.graph2d.GraphComponentShape;
import xj.graph2d.GraphDocument;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.GraphView;
import xj.graph2d.Group;
import xj.graph2d.Label;
import xj.graph2d.TextShape;
import xj.graph2d.command.AddNodeCommand;
import xj.graph2d.command.AddShapeCommand;
import xj.graph2d.command.LabelAuxAction;
import xj.graph2d.command.SubshapeAuxAction;
import xj.util.gui.Point2DFloat;

public class OnePointShapeTool 
  extends SimpleDrawTool 
  implements OnePointTool {

  public OnePointShapeTool(DrawShape shape) {
    this(shape, null, DrawShapeConstants.USE_SYSTEM_DRAW_ATTR);
  }

  public OnePointShapeTool(DrawShape shape, int cloneDrawAttrMode) {
    this(shape, null, cloneDrawAttrMode);
  }

  public OnePointShapeTool(DrawShape shape, DrawShapeListener listener) {
    this(shape, listener, DrawShapeConstants.USE_SYSTEM_DRAW_ATTR);
  }

  public OnePointShapeTool(DrawShape shape, DrawShapeListener listener,
			   int cloneDrawAttrMode) {
    super(cloneDrawAttrMode);
    this.listener = listener;
    this.shape = shape;
    firstMove = true;    
  }

  public void setPrototype(DrawShape shape) { 
    this.shape = shape;
    firstMove = true;
  }

  @Override
  public void mouseLButtonPressed(Observer observer, Point p, int modifiers) {
    start(observer);
    if (view != null) {
      Point2D cur = view.physicalToVirtual(p);
      DrawShape selected = doc.getSelectedShape();
      doc.setSelectedShape(null);
      boolean atomic = shape.isAtomic();
      DrawShape newShape = null; 

      if (shape instanceof Group) {
	Group g = (Group) shape;
	if (g.isTemporary()) {
	  Point2D cg = g.getCenterPosition();
	  ListIterator<DrawShape> iter = g.allShapes();
	  while (iter.hasNext()) {
	    DrawShape s = iter.next();
	    if (s != null) {
	      Point2D cs = s.getCenterPosition();
	      float dx = (float) (cs.getX() - cg.getX());
	      float dy = (float) (cs.getY() - cg.getY());
	      Point2D cur1 = new Point2DFloat((float) cur.getX() + dx,
					      (float) cur.getY() + dy);
	      newShape = addNewShape(s, cur1);
	    }
	  }
	} else {
	  newShape = addNewShape(g, cur);
	}
      } else {
	newShape = addNewShape(shape, cur);
      }

      if (switchTool != null) {
	switchTool();
	switchTool.setActionShape(newShape);
	switchTool.mouseLButtonPressed(observer, p, 0);
	switchTool.mouseLButtonReleased(observer, p, 0);
      }

      canvas.clearRepaintShapes();
      canvas.repaint();
    }
    firstMove = true;
  }

  protected DrawShape addNewShape(DrawShape proto, Point2D cur) {
    if (proto != null) {
      DrawShape newShape = DrawShapeFactory.cloneDrawShape(proto, view,
							   cloneDrawAttrMode);
      // newShape.setAtomic(true);
      if (newShape instanceof TextShape) {
	TextShape tshape = (TextShape) newShape;
	tshape.setBasePosition(cur);
      } else {
	newShape.setCenterPosition(cur);
      }
      if (listener != null) {
	listener.notify(newShape);
      }

      if (!newShape.isZeroSize()) {
	Command cmd;
	if (graphMode && 
	    doc instanceof GraphDocument && 
	    view instanceof GraphView) {
	  GraphDocument graph = (GraphDocument) doc;
	  GraphView gview = (GraphView) view;
	  if (shape instanceof GraphNodeBase) {
	    GraphNodeBase newNode = (GraphNodeBase) newShape;
	    cmd = new AddNodeCommand(gview, graph, newNode);
	  } else {
	    cmd = new AddShapeCommand(view, doc, newShape);
	    if (newShape instanceof Label && 
		(newShape.getRole() & ROLE_LABEL) != 0) {
	      Label label = (Label) newShape;
	      GraphComponentShape newParent = graph.searchHotSpot(cur);
	      int role = DrawShapeConstants.ROLE_DEFAULT;
	      if (newParent != null) {
		int hotspot = newParent.isOnHotSpot(cur);
		role = GraphComponentShape.hotspotToRole(hotspot);
		Label label1 = newParent.getLabelShape(role);
		newParent.setLabel(role, label);
		if (label1 != null) {
		  doc.shapeRemoved(label1);
		}
		cmd.addAuxAction(new LabelAuxAction(doc, null, newParent,
						    label, label1, -1, role, 
						    LabelAuxAction.ATTACH_LABEL));
	      }
	    }
	  }
	} else {
	  cmd = new AddShapeCommand(view, doc, newShape);
	  DrawShape newParent = doc.search(cur, newShape);
	  if (newParent != null && newParent.isAcceptSubshape(newShape)) {
	    cmd.addAuxAction(new SubshapeAuxAction(doc, null, newParent,
						   newShape, 
						   SubshapeAuxAction.ATTACH_SUBSHAPE));
	  }
	}
	doc.executeCommand(cmd);
	return newShape;
      }
    }
    return null;
  }

  @Override
  public void popupTriggered(Observer observer, Point p, int modifiers) {
    //System.out.println("OnePointShapeTool.popupTriggered()");
    if (view != null && shape != null) {
      canvas.clearRepaintShapes();
      canvas.repaint();
      firstMove = true;
    }
    if (switchTool != null) {
      switchTool();
    }
  }

  @Override
  public void mouseEntered(Observer observer, Point p, int modifiers) {
    start(observer);
    // firstMove = true;
    canvas.clearRepaintShapes();
    if (view != null && shape != null) {
      Point2D cur = view.physicalToVirtual(p);
      if (shape instanceof TextShape) {
	TextShape tshape = (TextShape) shape;
	tshape.setBasePosition(cur);
      } else {
	shape.setCenterPosition(cur);
      }
      canvas.addRepaintShape(shape);
      firstMove = false; 
    }
    canvas.repaint();
  }

  @Override
  public void mouseExited(Observer observer, Point p, int modifiers) {
    if (view != null && shape != null) {
      canvas.clearRepaintShapes();
      canvas.repaint();
      firstMove = true;
    }
  }

  @Override
  public void mouseMoved(Observer observer, Point p, int modifiers) {
    super.mouseMoved(observer, p, modifiers);
    if (view != null && shape != null) {
      Point2D cur = view.physicalToVirtual(p);
      if (shape instanceof TextShape) {
	TextShape tshape = (TextShape) shape;
	tshape.setBasePosition(cur);
      } else {
	shape.setCenterPosition(cur);
      }

      if (firstMove) {
	canvas.clearRepaintShapes();
	canvas.addRepaintShape(shape);
      }

      prev = cur;
      canvas.repaint();
    }
    firstMove = false;
  }

  @Override
  public void mouseDragged(Observer observer, Point p, int modifiers) {
    super.mouseDragged(observer, p, modifiers);
  }

  @Override
  public void mouseLButtonReleased(Observer observer, Point p, int modifiers) {
    if (switchTool != null) {
      switchTool.mouseLButtonReleased(observer, p, 0);
    }
  }

  @Override
  public void activate(Observer observer) {
    super.activate(observer);
    firstMove = true; 
  }

  @Override
  public void deactivate(Observer observer) {
    //System.out.println("OnePointShapeTool.deactivate()");
    super.deactivate(observer);
    view = null; 
    firstMove = true; 
  }

  public DrawShapeListener getDrawShapeListener() {
    return listener;
  }

  public void setDrawShapeListener(DrawShapeListener listener) {
    this.listener = listener;
  }

  protected DrawShape shape;

  protected DrawShapeListener listener;

  protected boolean firstMove = true;

}
